package com.hyhy.hycloud.commons.web.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author lanpengfei
 * @date 2018-07-31
 */
@Configuration
public class WebDateConfig {

    @Bean
    @ConditionalOnClass(ObjectMapper.class)
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        objectMapper.setDateFormat(new WebDateFormat(objectMapper.getDateFormat()));
        return new MappingJackson2HttpMessageConverter(objectMapper);
    }

    /**
     * WebDateFormat不能继承StdDateFormat，
     * 在注入ObjectMapper后，_force方法会判断instanceof StdDateFormat，
     * 将强制使用StdDateFormat的实现
     */
    protected static class WebDateFormat extends SimpleDateFormat {
        private static final String DATE_FORMAT_DAY = "yyyy-MM-dd";
        private static final String DATE_FORMAT_HOUR = "yyyy-MM-dd HH";
        private static final String DATE_FORMAT_MINUTE = "yyyy-MM-dd HH:mm";
        private static final String DATE_FORMAT_SECOND = "yyyy-MM-dd HH:mm:ss";

        private static final String DATE_FORMAT_DAY_REGEX = "^\\d{4}-\\d{1,2}-\\d{1,2}$";
        private static final String DATE_FORMAT_HOUR_REGEX = "^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}$";
        private static final String DATE_FORMAT_MINUTE_REGEX = "^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}$";
        private static final String DATE_FORMAT_SECOND_REGEX = "^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$";

        private static final String SEPARATOR_SLASH = "/";
        private static final String SEPARATOR_HYPHEN = "-";

        private DateFormat dateFormat;

        protected WebDateFormat(DateFormat dateFormat) {
            super();
            this.dateFormat = dateFormat;
        }

        /**
         * 优先使用注入的StdDateFormat的实现
         * 最后使用SimpleDateFormat的实现
         */
        @Override
        public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
            if (dateFormat != null) {
                return dateFormat.format(date, toAppendTo, pos);
            }
            return super.format(date, toAppendTo, pos);
        }

        /**
         * 优先使用WebDateFormat自定义的实现
         * 其次使用注入的StdDateFormat的实现
         * 最后使用SimpleDateFormat的实现
         */
        @Override
        public Date parse(String source, ParsePosition pos) {
            source = init(source);
            DateFormat df = initDateFormat(source);

            if (df != null) {
                return df.parse(source, pos);
            }
            return super.parse(source, pos);
        }


        @Override
        public Date parse(String source) {
            ParsePosition pos = new ParsePosition(0);
            return parse(source, pos);
        }

        @Override
        public Object clone() {
            WebDateFormat webDateFormat = (WebDateFormat) super.clone();
            if (dateFormat != null) {
                webDateFormat.dateFormat = (DateFormat) dateFormat.clone();
            }
            return webDateFormat;
        }

        private String init(String source) {
            source = source.trim();
            return source.contains(SEPARATOR_SLASH) ? source.replaceAll(SEPARATOR_SLASH, SEPARATOR_HYPHEN) : source;
        }

        private DateFormat initDateFormat(String source) {
            DateFormat df;
            if (source.matches(DATE_FORMAT_DAY_REGEX)) {
                df = new SimpleDateFormat(DATE_FORMAT_DAY);
            } else if (source.matches(DATE_FORMAT_HOUR_REGEX)) {
                df = new SimpleDateFormat(DATE_FORMAT_HOUR);
            } else if (source.matches(DATE_FORMAT_MINUTE_REGEX)) {
                df = new SimpleDateFormat(DATE_FORMAT_MINUTE);
            } else if (source.matches(DATE_FORMAT_SECOND_REGEX)) {
                df = new SimpleDateFormat(DATE_FORMAT_SECOND);
            } else {
                df = dateFormat;
            }
            return df;
        }
    }
}